import os
import sys
localDir = os.path.dirname(__file__)
curpath = os.path.normpath(os.path.join(os.getcwd(), localDir))
grandparent = os.path.normpath(os.path.join(curpath, '../'))
sys.path.insert(0, grandparent)

import unittest
from mock_db import Mockdb

from pymigrate import PyMigrate


class TestPyMigrate(unittest.TestCase):

    def tearDown(self):
        delete_migration_directory()

    def testTruth(self):
        self.assertTrue(True)

    def testInitializeWithGivenConfig(self):
        pm = PyMigrate(default_config())
        self.assertEqual(default_config(), pm.config)

    def testGetNumberWithCorrectFormat(self):
        pm = PyMigrate(default_config())
        filename = "001_test_me.ini"
        num = pm._get_number(filename)
        self.assertEqual("001", num)

    def testVerifyUniqueFileNumberInMigrations(self):
        pm = PyMigrate(default_config())
        files = ['001_test_one.ini', '002_test_two.ini']
        self.assertTrue(pm._verify_unique('001_test_one.ini', files))

    def testVerifyUniqueFileNumberInMigrationsThatNotUnique(self):
        pm = PyMigrate(default_config())
        files = ['001_test_one.ini', '001_test_two.ini']
        self.assertRaises(PyMigrate.DuplicateMigrationException,
            pm._verify_unique,
            **{"filename" : '001_test_one.ini', "files" : files})

    def testFindAllTheMigrationFilesInTheMigrationFolder(self):
        build_migration_directory()
        build_file("001_file.ini")
        build_file("002_file.ini")
        pm = PyMigrate(default_config())
        files = pm._build_files_list('development')
        self.assertEquals([{'number': '001', 'file': '001_file.ini'},
                           {'number': '002', 'file': '002_file.ini'}]
                         , files)

    def testFindAllTheMigrationFilesInTheMigrationFolderWithNonMigrations(self):
        build_migration_directory()
        build_file("001_file.ini")
        build_file("002_file.ini")
        build_file("README.txt")
        pm = PyMigrate(default_config())
        files = pm._build_files_list('development')
        self.assertEquals([{'number': '001', 'file': '001_file.ini'},
                           {'number': '002', 'file': '002_file.ini'}]
                         , files)

    def testParseCommandFromFile(self):
        pm = PyMigrate(default_config())
        cmdline = "sql: SELECT *"
        result = pm._parse_command(cmdline)
        self.assertEquals(('sql', 'SELECT *'), result)

    def testParseFilesWithTwoMigrationsUpVersion(self):
        build_migration_directory()
        build_file("001_file.ini", build_test_one_file())
        build_file("002_file.ini", build_test_two_file())
        pm = PyMigrate(default_config())
        queries = pm._parse_file("[up]", "[down]", "001_file.ini", "development")
        self.assertEquals( [{'data': 'SELECT*\nFROM\nschema_info\n', 'command': 'sql'}],
                          queries)

        queries = pm._parse_file("[up]", "[down]", "002_file.ini", "development")
        self.assertEquals([{'data': 'SELECT * FROM accounts', 'command': 'sql'}],
                          queries)

    def testParseFilesWithTwoMigrationsDownVersion(self):
        build_migration_directory()
        build_file("001_file.ini", build_test_one_file())
        build_file("002_file.ini", build_test_two_file())
        pm = PyMigrate(default_config())
        queries = pm._parse_file("[down]", "[up]", "001_file.ini", "development")
        self.assertEquals([{'data': 'SELECT * FROM users', 'command': 'sql'}],
                          queries)

        queries = pm._parse_file("[down]", "[up]", "002_file.ini", "development")
        self.assertEquals([{'data': 'SELECT * FROM links', 'command': 'sql'}],
                          queries)

    def testDetermineStopVersionStarting000Stopping002With003file(self):
        build_migration_directory()
        build_file("001_file.ini", build_test_one_file())
        build_file("002_file.ini", build_test_two_file())
        build_file("003_file.ini", build_test_two_file())
        pm = PyMigrate(default_config())
        files = pm._build_files_list('development')
        control = pm._determine_stop_version(000, 002, files)
        self.assertEquals({'start': 0, 'stop': 2, 'walk': 'up'}, control)

    def testDetermineStopVersionStarting000Stopping001With003file(self):
        build_migration_directory()
        build_file("001_file.ini", build_test_one_file())
        build_file("002_file.ini", build_test_two_file())
        build_file("003_file.ini", build_test_two_file())
        pm = PyMigrate(default_config())
        files = pm._build_files_list('development')
        control = pm._determine_stop_version(000, 001, files)
        self.assertEquals({'start': 0, 'stop': 1, 'walk': 'up'}, control)

    def testDetermineStopVersionStarting003Stopping001(self):
        build_migration_directory()
        build_file("001_file.ini", build_test_one_file())
        build_file("002_file.ini", build_test_two_file())
        build_file("003_file.ini", build_test_two_file())
        pm = PyMigrate(default_config())
        files = pm._build_files_list('development')
        control = pm._determine_stop_version(003, 001, files)
        self.assertEquals({'start': 3, 'stop': 1, 'walk': 'down'}, control)

    def testDetermineStopVersionStarting003Stopping003(self):
        build_migration_directory()
        build_file("001_file.ini", build_test_one_file())
        build_file("002_file.ini", build_test_two_file())
        build_file("003_file.ini", build_test_two_file())
        pm = PyMigrate(default_config())
        files = pm._build_files_list('development')
        control = pm._determine_stop_version(003, 003, files)
        self.assertEquals({'start': 3, 'stop': 3, 'walk': 'up'}, control)

    def testDetermineStopVersionStarting001WithNoOptionVersion(self):
        build_migration_directory()
        build_file("001_file.ini", build_test_one_file())
        build_file("002_file.ini", build_test_two_file())
        build_file("003_file.ini", build_test_two_file())
        pm = PyMigrate(default_config())
        files = pm._build_files_list('development')
        control = pm._determine_stop_version(001, None, files)
        self.assertEquals({'start': 1, 'stop': 3, 'walk': 'up'}, control)

    def testRunFilesWithOutError(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")
        build_file("002_file.ini", "[up]\n[down]")
        build_file("003_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())
        db = Mockdb()

        files = pm._build_files_list('development')
        control = pm._determine_stop_version(000, None, files)

        pm._run_files(files, control, db, "development")

        self.assertEquals('003', db.get_version())

    def testVersionUp(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")
        build_file("002_file.ini", "[up]\n[down]")
        build_file("003_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())
        db = Mockdb()

        files = pm._build_files_list('development')
        control = pm._determine_stop_version(000, 002, files)

        self.assertTrue(pm._run_version_up(control, 0, files))
        self.assertTrue(pm._run_version_up(control, 1, files))
        self.assertFalse(pm._run_version_up(control, 2, files))

    def testVersionDown(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")
        build_file("002_file.ini", "[up]\n[down]")
        build_file("003_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())
        db = Mockdb()

        files = pm._build_files_list('development')
        control = pm._determine_stop_version(002, 000, files)

        self.assertFalse(pm._run_version_down(control, 0, files))
        self.assertTrue(pm._run_version_down(control, 1, files))
        self.assertTrue(pm._run_version_down(control, 2, files))

    def testExecuteFileUp(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())
        db = Mockdb()

        files = pm._build_files_list('development')
        control = pm._determine_stop_version(000, 001, files)

        pm._execute_file("[up]", "[down]", 0, files, db, 001, "development")

        self.assertEquals('001', db.get_version())

    def testExecuteFileDown(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())
        db = Mockdb()

        files = pm._build_files_list('development')
        control = pm._determine_stop_version(001, 000, files)

        pm._execute_file("[down]", "[up]", 0, files, db, 000, "development")

        self.assertEquals(0, db.get_version())

    def testExecuteFileOnErrorUp(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")
        build_file("002_file.ini", "[up]\nmock_code: raise\n[down]")
        build_file("003_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())
        db = Mockdb()

        files = pm._build_files_list('development')
        pm._execute_file("up", "down", 0, files, db, 003, "development")
        try:
            pm._execute_file("up", "down", 1, files, db, 003, "development")
        except:
            pass

        self.assertEquals('001', db.get_version())

    def testExecuteEndToEndUpWithNoVersionUpArgument(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")
        build_file("002_file.ini", "[up]\n[down]")
        build_file("003_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())
        
        self.assertEquals(3, pm.run(None, "development"))

    def testExecuteEndToEndUpWithVersionUpArgument(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")
        build_file("002_file.ini", "[up]\n[down]")
        build_file("003_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())

        self.assertEquals(2, pm.run(002, "development"))

    def testExecuteEndToEndUpNoVersionUpArgumentOnError(self):
        build_migration_directory()
        build_file("001_file.ini", "[up]\n[down]")
        build_file("002_file.ini", "[up]\nmock_code:\nraise[down]")
        build_file("003_file.ini", "[up]\n[down]")

        pm = PyMigrate(default_config())

        self.assertEquals(1, pm.run(None, "development"))

    def testStartTheFileCommandsUp(self):
        pm = PyMigrate(default_config())
        self.assertTrue(pm._start_file_commands_recording("[up]", "[up]"))
        self.assertFalse(pm._start_file_commands_recording("[up]", "[down]"))

    def testStopTheFileCommandsDown(self):
        pm = PyMigrate(default_config())
        self.assertTrue(pm._stop_file_commands_recording("[down]", "[down]"))
        self.assertFalse(pm._stop_file_commands_recording("[down]","[up]"))

    def testBuildQueriesList(self):
        queries = []
        pm = PyMigrate(default_config())
        pm._build_queries_list(queries, "code: print")
        self.assertEquals([{'data': 'print', 'command': 'code'}], queries)
        pm._build_queries_list(queries, "code: print")
        self.assertEquals([{'data': 'print', 'command': 'code'},
            {'data': 'print', 'command': 'code'}],
            queries)

def default_config():
    return {"development" :
            {"db" : "sqlite:///:memory:",
             "migration_path" : "./migrations/"}}

def build_migration_directory():
    os.mkdir('migrations')

def delete_migration_directory():
    if not os.access("migrations", os.X_OK):
        return
    
    for root, dirs, files in os.walk("migrations"):
        for file in files:
            os.remove('migrations/%s' % file)
    os.rmdir('migrations')

def build_file(filename, data=""):
    f = open("migrations/%s" % filename,"w")
    f.writelines(data)
    f.close()

def build_test_one_file():
    return """[up]
sql: SELECT
*
FROM
schema_info

[down]
sql: SELECT * FROM users
"""

def build_test_two_file():
    return """[up]
sql: SELECT * FROM accounts

[down]
sql: SELECT * FROM links
"""

if __name__ == "__main__":
    unittest.main()

